/******************************************************************************
  A simple program of Hisilicon Hi35xx video encode implementation.
  Copyright (C), 2018, Hisilicon Tech. Co., Ltd.
******************************************************************************/

#include <unistd.h>
#include <pthread.h>

#include "mpi_vpss.h"
#include "sample_comm.h"
// #include "sample_venc.h"
// #include "sample_yuv.h"
// #include "histream.h"
// #include "uvc_venc_glue.h"

#define PIC_SIZE PIC_720P

// #define v4l2_fourcc(a, b, c, d) \
//     ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))

// #define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V')   /* 16  YUV 4:2:2     */
// #define V4L2_PIX_FMT_YUV420 v4l2_fourcc('Y', 'U', '1', '2') /* 16  YUV 4:2:0     */
// #define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G')  /* Motion-JPEG   */
// #define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4')   /* H264 with start codes */

static SAMPLE_VI_CONFIG_S g_stViConfig;
static HI_U32 g_u32SupplementConfig = HI_FALSE;
static PIC_SIZE_E g_enSize = PIC_SIZE;
static HI_S32 g_s32ChnNum;
static HI_U8 __started = 0;
// extern encoder_property __encoder_property;
// extern HI_S32 __SAMPLE_StartGetYUV(HI_S32 s32VpssGrp, HI_S32 s32VpssChn);
// extern HI_S32 __SAMPLE_StopGetYUV(HI_VOID);


extern HI_S32 get_hisi_video_frame(VENC_CHN VencChn, VENC_STREAM_S* pstStream);









static pthread_t __gs_VencPid;
static SAMPLE_VENC_GETSTREAM_PARA_S __gs_stPara;

// #define v4l2_fourcc(a, b, c, d) \
//     ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))

// #define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V')  /* 16  YUV 4:2:2     */
// #define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG   */
// #define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4')  /* H264 with start codes */


#if 0
typedef struct encoder_property
{
    unsigned int format;
    unsigned int width;
    unsigned int height;
    unsigned char compsite;
} encoder_property;

encoder_property __encoder_property;

static PAYLOAD_TYPE_E change_to_mpp_format(uint32_t fcc)
{
    PAYLOAD_TYPE_E t;

    switch (fcc)
    {
    case V4L2_PIX_FMT_YUYV:
    case V4L2_PIX_FMT_MJPEG:
        t = PT_MJPEG;
        break;

    case V4L2_PIX_FMT_H264:
        t = PT_H264;
        break;

    default:
        t = PT_MJPEG;
        break;
    }

    return t;
}

static PIC_SIZE_E change_to_mpp_wh(int width)
{
    PIC_SIZE_E s = PIC_720P;

    switch (width)
    {
    case 640:
#ifndef __HuaweiLite__
        s = PIC_360P; /*640 x 360*/
#else
        s = PIC_VGA;
#endif
        break;
    case 1280:
        s = PIC_720P;
        break;
    case 1920:
        s = PIC_1080P;
        break;
    case 3840:
        s = PIC_3840x2160;
        break;
    default:
        s = PIC_720P;
        break;
    }

    return s;
}

static HI_S32 SAMPLE_COMM_VENC_GetFilePostfix(PAYLOAD_TYPE_E enPayload, char *szFilePostfix)
{
    if (PT_H264 == enPayload)
    {
        strcpy(szFilePostfix, ".h264");
    }
    else if (PT_H265 == enPayload)
    {
        strcpy(szFilePostfix, ".h265");
    }
    else if (PT_JPEG == enPayload)
    {
        strcpy(szFilePostfix, ".jpg");
    }
    else if (PT_MJPEG == enPayload)
    {
        strcpy(szFilePostfix, ".mjp");
    }
    else
    {
        SAMPLE_PRT("payload type err!\n");
        return HI_FAILURE;
    }
    return HI_SUCCESS;
}

static HI_VOID set_config_format(PAYLOAD_TYPE_E *format, int idx)
{
    format[idx] = change_to_mpp_format(__encoder_property.format);
}

static HI_VOID set_config_wh(PIC_SIZE_E *wh, int idx)
{
    wh[idx] = change_to_mpp_wh(__encoder_property.width);
}

HI_VOID set_user_config_format(PAYLOAD_TYPE_E *format, PIC_SIZE_E *wh, int *c)
{
    set_config_format(format, 0);
    set_config_wh(wh, 0);
    *c = 1;
}
#endif

#if 0
int set_encoder_property(encoder_property *p)
{
    __encoder_property = *p;
    set_yuv_property();

    return 0;
}

int is_channel_yuv(int channel)
{
    if ((channel == 1) && (__encoder_property.format == V4L2_PIX_FMT_YUYV))
    {
        return 1;
    }

    return 0;
}

HI_S32 __SAMPLE_COMM_VENC_FORMAT(HI_VOID)
{
    if (__encoder_property.format == V4L2_PIX_FMT_YUYV)
    {
        return PIXEL_FORMAT_YVU_SEMIPLANAR_422;
    }
    else
    {
        return PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    }
}
#endif

#if 0
/******************************************************************************
* funciton : save h264/h265/mjpeg stream.
******************************************************************************/
static HI_S32 __SAMPLE_COMM_VENC_SaveData(VENC_STREAM_S *pstStream)
{
    HI_U32 i = 0;
    VENC_PACK_S *pstData = HI_NULL;
    unsigned char *s = HI_NULL;
    unsigned int data_len = 0;
    unsigned int copy_size = 0;

    uvc_cache_t *uvc_cache = uvc_cache_get();
    frame_node_t *fnode = HI_NULL;

    if (uvc_cache)
    {
        get_node_from_queue(uvc_cache->free_queue, &fnode);
    }

    if (!fnode)
    {
        return HI_SUCCESS;
    }

    fnode->used = 0;

    for (i = 0; i < pstStream->u32PackCount; ++i)
    {
        pstData = &pstStream->pstPack[i];
        s = pstData->pu8Addr + pstData->u32Offset;
        data_len = pstData->u32Len - pstData->u32Offset;
        copy_size = data_len < (fnode->length - fnode->used) ? data_len : (fnode->length - fnode->used);

        if (copy_size > 0)
        {
            memcpy(fnode->mem + fnode->used, s, copy_size);
            fnode->used += copy_size;
        }
    }

    put_node_to_queue(uvc_cache->ok_queue, fnode);

    return HI_SUCCESS;
}

/******************************************************************************
* funciton : save stream
******************************************************************************/
static HI_S32 __SAMPLE_COMM_VENC_SaveStream(PAYLOAD_TYPE_E enType, FILE *pFd, VENC_STREAM_S *pstStream)
{
    HI_S32 s32Ret = HI_FAILURE;
#if (1 == UVC_SAVE_FILE)
    HI_S32 i = 0;
#endif

    if (PT_H264 == enType || PT_MJPEG == enType || PT_H265 == enType)
    {
        s32Ret = __SAMPLE_COMM_VENC_SaveData(pstStream);
    }

#if (1 == UVC_SAVE_FILE)
    for (i = 0; i < pstStream->u32PackCount; ++i)
    {
        fwrite(pstStream->pstPack[i].pu8Addr + pstStream->pstPack[i].u32Offset,
               pstStream->pstPack[i].u32Len - pstStream->pstPack[i].u32Offset, 1, pFd);

        fflush(pFd);
    }
#endif

    return s32Ret;
}
#endif

/******************************************************************************
* funciton : get stream from each channels and save them
******************************************************************************/
HI_VOID *__SAMPLE_COMM_VENC_GetVencStreamProc(HI_VOID *p)
{
    HI_S32 i;
    HI_S32 s32ChnTotal;
    VENC_CHN_ATTR_S stVencChnAttr;
    SAMPLE_VENC_GETSTREAM_PARA_S *pstPara;
    HI_S32 maxfd = 0;
    struct timeval TimeoutVal;
    fd_set read_fds;
    HI_U32 u32PictureCnt[VENC_MAX_CHN_NUM] = {0};
    HI_S32 VencFd[VENC_MAX_CHN_NUM];
#if (1 == UVC_SAVE_FILE)
    HI_CHAR aszFileName[VENC_MAX_CHN_NUM][64];
#endif
    FILE *pFile[VENC_MAX_CHN_NUM];
    char szFilePostfix[10];
    VENC_CHN_STATUS_S stStat;
    VENC_STREAM_S stStream;
    HI_S32 s32Ret;
    VENC_CHN VencChn;
    PAYLOAD_TYPE_E enPayLoadType[VENC_MAX_CHN_NUM];
    VENC_STREAM_BUF_INFO_S stStreamBufInfo[VENC_MAX_CHN_NUM];

    pstPara = (SAMPLE_VENC_GETSTREAM_PARA_S *)p;
    s32ChnTotal = pstPara->s32Cnt;

    /******************************************
     step 1:  check & prepare save-file & venc-fd
    ******************************************/
    if (s32ChnTotal >= VENC_MAX_CHN_NUM)
    {
        SAMPLE_PRT("input count invaild\n");
        return HI_NULL;
    }
    for (i = 0; i < s32ChnTotal; i++)
    {
        /* decide the stream file name, and open file to save stream */
        VencChn = pstPara->VeChn[i];
        s32Ret = HI_MPI_VENC_GetChnAttr(VencChn, &stVencChnAttr);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("HI_MPI_VENC_GetChnAttr chn[%d] failed with %#x!\n",
                       VencChn, s32Ret);
            return HI_NULL;
        }
        enPayLoadType[i] = stVencChnAttr.stVencAttr.enType;

        s32Ret = SAMPLE_COMM_VENC_GetFilePostfix(enPayLoadType[i], szFilePostfix);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("SAMPLE_COMM_VENC_GetFilePostfix [%d] failed with %#x!\n",
                       stVencChnAttr.stVencAttr.enType, s32Ret);
            return HI_NULL;
        }
#if (1 == UVC_SAVE_FILE)
        if (PT_JPEG != enPayLoadType[i])
        {
            snprintf(aszFileName[i], 32, "output/stream_chn%d%s", i, szFilePostfix);

            pFile[i] = fopen(aszFileName[i], "wb");
            if (!pFile[i])
            {
                SAMPLE_PRT("open file[%s] failed!\n",
                           aszFileName[i]);
                return HI_NULL;
            }
        }
#endif
        /* Set Venc Fd. */
        VencFd[i] = HI_MPI_VENC_GetFd(i);
        if (VencFd[i] < 0)
        {
            SAMPLE_PRT("HI_MPI_VENC_GetFd failed with %#x!\n",
                       VencFd[i]);
            return HI_NULL;
        }
        if (maxfd <= VencFd[i])
        {
            maxfd = VencFd[i];
        }

        s32Ret = HI_MPI_VENC_GetStreamBufInfo(i, &stStreamBufInfo[i]);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("HI_MPI_VENC_GetStreamBufInfo failed with %#x!\n", s32Ret);
            return (HI_VOID *)HI_FAILURE;
        }
    }

    /******************************************
     step 2:  Start to get streams of each channel.
    ******************************************/
    while (HI_TRUE == pstPara->bThreadStart)
    {
        FD_ZERO(&read_fds);
        for (i = 0; i < s32ChnTotal; i++)
        {
            FD_SET(VencFd[i], &read_fds);
        }

        TimeoutVal.tv_sec = 2;
        TimeoutVal.tv_usec = 0;
        s32Ret = select(maxfd + 1, &read_fds, HI_NULL, HI_NULL, &TimeoutVal);
        if (s32Ret < 0)
        {
            SAMPLE_PRT("select failed!\n");
            break;
        }
        else if (s32Ret == 0)
        {
            SAMPLE_PRT("get venc stream time out, exit thread\n");
            continue;
        }
        else
        {
            for (i = 0; i < s32ChnTotal; i++)
            {
                if (FD_ISSET(VencFd[i], &read_fds))
                {
                    /*******************************************************
                     step 2.1 : query how many packs in one-frame stream.
                    *******************************************************/
                    memset(&stStream, 0, sizeof(stStream));

                    s32Ret = HI_MPI_VENC_QueryStatus(i, &stStat);
                    if (HI_SUCCESS != s32Ret)
                    {
                        SAMPLE_PRT("HI_MPI_VENC_QueryStatus chn[%d] failed with %#x!\n", i, s32Ret);
                        break;
                    }

                    /*******************************************************
                     step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,for example:
                     if (0 == stStat.u32CurPacks || 0 == stStat.u32LeftStreamFrames)
                     {
                     SAMPLE_PRT("NOTE: Current frame is HI_NULL!\n");
                     continue;
                     }
                    *******************************************************/
                    if (0 == stStat.u32CurPacks)
                    {
                        SAMPLE_PRT("NOTE: Current frame is HI_NULL!\n");
                        continue;
                    }
                    /*******************************************************
                     step 2.3 : malloc corresponding number of pack nodes.
                    *******************************************************/
                    stStream.pstPack = (VENC_PACK_S *)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);
                    if (HI_NULL == stStream.pstPack)
                    {
                        SAMPLE_PRT("malloc stream pack failed!\n");
                        break;
                    }

                    /*******************************************************
                     step 2.4 : call mpi to get one-frame stream
                    *******************************************************/
                    stStream.u32PackCount = stStat.u32CurPacks;
                    s32Ret = HI_MPI_VENC_GetStream(i, &stStream, HI_TRUE);
                    if (HI_SUCCESS != s32Ret)
                    {
                        free(stStream.pstPack);
                        stStream.pstPack = HI_NULL;
                        SAMPLE_PRT("HI_MPI_VENC_GetStream failed with %#x!\n", s32Ret);
                        break;
                    }

                    /*******************************************************
                     step 2.5 : save frame to file
                    *******************************************************/
#if (1 == UVC_SAVE_FILE)
                    if (PT_JPEG == enPayLoadType[i])
                    {
                        snprintf(aszFileName[i], 32, "output/stream_chn%d_%d%s", i, u32PictureCnt[i], szFilePostfix);
                        pFile[i] = fopen(aszFileName[i], "wb");
                        if (!pFile[i])
                        {
                            SAMPLE_PRT("open file err!\n");
                            return HI_NULL;
                        }
                    }
#endif

                    // s32Ret = __SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], &stStream);
					get_hisi_video_frame(i, &stStream);

                    if (HI_SUCCESS != s32Ret)
                    {
                        free(stStream.pstPack);
                        stStream.pstPack = HI_NULL;
                        SAMPLE_PRT("save stream failed!\n");
                        break;
                    }

                    /*******************************************************
                     step 2.6 : release stream
                    *******************************************************/
                    s32Ret = HI_MPI_VENC_ReleaseStream(i, &stStream);
                    if (HI_SUCCESS != s32Ret)
                    {
                        SAMPLE_PRT("HI_MPI_VENC_ReleaseStream failed!\n");
                        free(stStream.pstPack);
                        stStream.pstPack = HI_NULL;
                        break;
                    }

                    /*******************************************************
                     step 2.7 : free pack nodes
                    *******************************************************/
                    free(stStream.pstPack);
                    stStream.pstPack = HI_NULL;
                    u32PictureCnt[i]++;
                    if (PT_JPEG == enPayLoadType[i])
                    {
                        fclose(pFile[i]);
                    }
                }
            }
        }
    }
    /*******************************************************
     step 3 : close save-file
    *******************************************************/
#if (1 == UVC_SAVE_FILE)
    for (i = 0; i < s32ChnTotal; i++)
    {
        if (PT_JPEG != enPayLoadType[i])
        {
            fclose(pFile[i]);
        }
    }
#endif
    return HI_NULL;
}

HI_S32 __SAMPLE_COMM_VENC_StartGetStream(VENC_CHN VeChn[], HI_S32 s32Cnt)
{
    HI_S32 i = 0;

    __gs_stPara.bThreadStart = HI_TRUE;
    __gs_stPara.s32Cnt = s32Cnt;
    for (i = 0; i < s32Cnt; ++i)
    {
        __gs_stPara.VeChn[i] = VeChn[i];
    }

    return pthread_create(&__gs_VencPid, 0, __SAMPLE_COMM_VENC_GetVencStreamProc, (HI_VOID *)&__gs_stPara);
}

/******************************************************************************
* funciton : stop get venc stream process.
******************************************************************************/
HI_S32 __SAMPLE_COMM_VENC_StopGetStream(HI_VOID)
{
    if (HI_TRUE == __gs_stPara.bThreadStart)
    {
        __gs_stPara.bThreadStart = HI_FALSE;
        pthread_join(__gs_VencPid, 0);
    }
    return HI_SUCCESS;
}














static HI_S32 __SAMPLE_VENC_VPSS_Init(VPSS_GRP VpssGrp, HI_BOOL *pabChnEnable, DYNAMIC_RANGE_E enDynamicRange,
                                      PIXEL_FORMAT_E enPixelFormat, SIZE_S stSize[], SAMPLE_SNS_TYPE_E enSnsType)
{
    HI_S32 i;
    HI_S32 s32Ret;
    PIC_SIZE_E enSnsSize;
    SIZE_S stSnsSize;
    VPSS_GRP_ATTR_S stVpssGrpAttr = {0};
    VPSS_CHN_ATTR_S stVpssChnAttr[VPSS_MAX_PHY_CHN_NUM];

    s32Ret = SAMPLE_COMM_VI_GetSizeBySensor(enSnsType, &enSnsSize);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("SAMPLE_COMM_VI_GetSizeBySensor failed!\n");
        return s32Ret;
    }

    s32Ret = SAMPLE_COMM_SYS_GetPicSize(enSnsSize, &stSnsSize);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        return s32Ret;
    }

    stVpssGrpAttr.enDynamicRange = enDynamicRange;
    stVpssGrpAttr.enPixelFormat = enPixelFormat;
    stVpssGrpAttr.u32MaxW = stSnsSize.u32Width;
    stVpssGrpAttr.u32MaxH = stSnsSize.u32Height;
    stVpssGrpAttr.stFrameRate.s32SrcFrameRate = -1;
    stVpssGrpAttr.stFrameRate.s32DstFrameRate = -1;
    stVpssGrpAttr.bNrEn = HI_TRUE;
    stVpssGrpAttr.stNrAttr.enNrType = VPSS_NR_TYPE_VIDEO;
    stVpssGrpAttr.stNrAttr.enNrMotionMode = NR_MOTION_MODE_NORMAL;
    stVpssGrpAttr.stNrAttr.enCompressMode = COMPRESS_MODE_FRAME;

    for (i = 0; i < VPSS_MAX_PHY_CHN_NUM; ++i)
    {
        if (HI_TRUE == pabChnEnable[i])
        {
            stVpssChnAttr[i].u32Width = stSize[i].u32Width;
            stVpssChnAttr[i].u32Height = stSize[i].u32Height;
            stVpssChnAttr[i].enChnMode = VPSS_CHN_MODE_USER;
            stVpssChnAttr[i].enCompressMode = COMPRESS_MODE_NONE;
            stVpssChnAttr[i].enDynamicRange = enDynamicRange;
            stVpssChnAttr[i].enPixelFormat = enPixelFormat;
            stVpssChnAttr[i].stFrameRate.s32SrcFrameRate = -1;
            stVpssChnAttr[i].stFrameRate.s32DstFrameRate = -1;
            stVpssChnAttr[i].u32Depth = 0;
            stVpssChnAttr[i].bMirror = HI_FALSE;
            stVpssChnAttr[i].bFlip = HI_FALSE;
            stVpssChnAttr[i].enVideoFormat = VIDEO_FORMAT_LINEAR;
            stVpssChnAttr[i].stAspectRatio.enMode = ASPECT_RATIO_NONE;
        }
    }

    s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, pabChnEnable, &stVpssGrpAttr, stVpssChnAttr);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("start VPSS fail for %#x!\n", s32Ret);
    }

    return s32Ret;
}

static HI_S32 __SAMPLE_VENC_CheckSensor(SAMPLE_SNS_TYPE_E enSnsType, SIZE_S stSize)
{
    HI_S32 s32Ret;
    SIZE_S stSnsSize;
    PIC_SIZE_E enSnsSize;

    s32Ret = SAMPLE_COMM_VI_GetSizeBySensor(enSnsType, &enSnsSize);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("SAMPLE_COMM_VI_GetSizeBySensor failed!\n");
        return s32Ret;
    }
    s32Ret = SAMPLE_COMM_SYS_GetPicSize(enSnsSize, &stSnsSize);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        return s32Ret;
    }

    if ((stSnsSize.u32Width < stSize.u32Width) || (stSnsSize.u32Height < stSize.u32Height))
    {
        SAMPLE_PRT("Sensor size is (%d,%d), but encode chnl is (%d,%d)!\n",
                   stSnsSize.u32Width, stSnsSize.u32Height, stSize.u32Width, stSize.u32Height);
        return HI_FAILURE;
    }

    return HI_SUCCESS;
}

static HI_S32 __SAMPLE_VENC_SYS_Init(HI_U32 u32SupplementConfig, SAMPLE_SNS_TYPE_E enSnsType)
{
    HI_S32 s32Ret;
    HI_U64 u64BlkSize;
    VB_CONFIG_S stVbConf;
    PIC_SIZE_E enSnsSize;
    SIZE_S stSnsSize;

    memset(&stVbConf, 0, sizeof(VB_CONFIG_S));

    s32Ret = SAMPLE_COMM_VI_GetSizeBySensor(enSnsType, &enSnsSize);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("SAMPLE_COMM_VI_GetSizeBySensor failed!\n");
        return s32Ret;
    }

    s32Ret = SAMPLE_COMM_SYS_GetPicSize(enSnsSize, &stSnsSize);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        return s32Ret;
    }

    u64BlkSize = COMMON_GetPicBufferSize(stSnsSize.u32Width, stSnsSize.u32Height, PIXEL_FORMAT_YVU_SEMIPLANAR_422, DATA_BITWIDTH_8, COMPRESS_MODE_NONE, DEFAULT_ALIGN);
    stVbConf.astCommPool[0].u64BlkSize = u64BlkSize;
    stVbConf.astCommPool[0].u32BlkCnt = 3;

    u64BlkSize = COMMON_GetPicBufferSize(1920, 1080, PIXEL_FORMAT_YVU_SEMIPLANAR_422, DATA_BITWIDTH_8, COMPRESS_MODE_NONE,DEFAULT_ALIGN);
    stVbConf.astCommPool[1].u64BlkSize   = u64BlkSize;
    stVbConf.astCommPool[1].u32BlkCnt    = 2;
    
    u64BlkSize = COMMON_GetPicBufferSize(1280, 720, PIXEL_FORMAT_YVU_SEMIPLANAR_422, DATA_BITWIDTH_8, COMPRESS_MODE_NONE,DEFAULT_ALIGN);
    stVbConf.astCommPool[2].u64BlkSize   = u64BlkSize;
    stVbConf.astCommPool[2].u32BlkCnt    = 2;

    stVbConf.u32MaxPoolCnt = 3;

    if (0 == u32SupplementConfig)
    {
        s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
    }
    else
    {
        s32Ret = SAMPLE_COMM_SYS_InitWithVbSupplement(&stVbConf, u32SupplementConfig);
    }

    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        return s32Ret;
    }

    return HI_SUCCESS;
}

static HI_S32 __SAMPLE_VENC_VI_Init(SAMPLE_VI_CONFIG_S *pstViConfig, HI_U32 u32SupplementConfig)
{
    HI_S32 s32Ret;
    SAMPLE_SNS_TYPE_E enSnsType;
    ISP_CTRL_PARAM_S stIspCtrlParam;
    HI_U32 u32FrameRate;

    enSnsType = pstViConfig->astViInfo[0].stSnsInfo.enSnsType;

    pstViConfig->as32WorkingViId[0] = 0;
    pstViConfig->astViInfo[0].stSnsInfo.MipiDev = SAMPLE_COMM_VI_GetComboDevBySensor(pstViConfig->astViInfo[0].stSnsInfo.enSnsType, 0);
    pstViConfig->astViInfo[0].stSnsInfo.s32BusId = 0;
    pstViConfig->astViInfo[0].stDevInfo.enWDRMode = WDR_MODE_NONE;
    // pstViConfig->astViInfo[0].stPipeInfo.enMastPipeMode = VI_ONLINE_VPSS_ONLINE;
    pstViConfig->astViInfo[0].stPipeInfo.enMastPipeMode = VI_OFFLINE_VPSS_OFFLINE;
    // pstViConfig->astViInfo[0].stPipeInfo.enMastPipeMode = VI_OFFLINE_VPSS_ONLINE;
    pstViConfig->astViInfo[0].stPipeInfo.aPipe[1] = -1;
    pstViConfig->astViInfo[0].stChnInfo.enVideoFormat = VIDEO_FORMAT_LINEAR;
    pstViConfig->astViInfo[0].stChnInfo.enCompressMode = COMPRESS_MODE_NONE;

    s32Ret = SAMPLE_COMM_VI_SetParam(pstViConfig);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("SAMPLE_COMM_VI_SetParam failed with %d!\n", s32Ret);
        return s32Ret;
    }

    SAMPLE_COMM_VI_GetFrameRateBySensor(enSnsType, &u32FrameRate);

    s32Ret = HI_MPI_ISP_GetCtrlParam(pstViConfig->astViInfo[0].stPipeInfo.aPipe[0], &stIspCtrlParam);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_ISP_GetCtrlParam failed with %d!\n", s32Ret);
        return s32Ret;
    }
    stIspCtrlParam.u32StatIntvl = u32FrameRate / 30;

    s32Ret = HI_MPI_ISP_SetCtrlParam(pstViConfig->astViInfo[0].stPipeInfo.aPipe[0], &stIspCtrlParam);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("HI_MPI_ISP_SetCtrlParam failed with %d!\n", s32Ret);
        return s32Ret;
    }

    s32Ret = SAMPLE_COMM_VI_StartVi(pstViConfig);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_COMM_SYS_Exit();
        SAMPLE_PRT("SAMPLE_COMM_VI_StartVi failed with %d!\n", s32Ret);
        return s32Ret;
    }

    return HI_SUCCESS;
}

static HI_S32 __SAMPLE_VENC_NORMALP_CLASSIC(HI_VOID)
{
    HI_S32 s32Ret = HI_FAILURE;

    SIZE_S stSize[3];
    HI_S32 s32ChnNum = 1;
    VENC_CHN VencChn[2] = {0, 1};
    HI_U32 u32Profile = 0;
    PAYLOAD_TYPE_E enPayLoad = PT_H264;
    VENC_GOP_MODE_E enGopMode = VENC_GOPMODE_NORMALP;
    VENC_GOP_ATTR_S stGopAttr;
    SAMPLE_RC_E enRcMode = SAMPLE_RC_CBR;
    HI_BOOL bRcnRefShareBuf = HI_TRUE;

    VPSS_GRP VpssGrp = 0;
    VPSS_CHN VpssChn = 1;
    HI_BOOL abChnEnable[3] = {0, 1, 0};
    PIXEL_FORMAT_E enPixFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;

    // set_user_config_format(&enPayLoad, &g_enSize, &s32ChnNum);

    // if (V4L2_PIX_FMT_YUYV == __encoder_property.format)
    // {
    //     enPixFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_422;
    // }

    s32Ret = SAMPLE_COMM_SYS_GetPicSize(g_enSize, &stSize[VpssChn]);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("SAMPLE_COMM_SYS_GetPicSize failed!\n");
        return s32Ret;
    }

    SAMPLE_COMM_VI_GetSensorInfo(&g_stViConfig);
    if (SAMPLE_SNS_TYPE_BUTT == g_stViConfig.astViInfo[0].stSnsInfo.enSnsType)
    {
        SAMPLE_PRT("Not set SENSOR%d_TYPE !\n", 0);
        return HI_FAILURE;
    }

    s32Ret = __SAMPLE_VENC_CheckSensor(g_stViConfig.astViInfo[0].stSnsInfo.enSnsType, stSize[VpssChn]);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("Check Sensor err!\n");
        return HI_FAILURE;
    }

    /******************************************
     start SYS VI VPSS
    ******************************************/
    g_stViConfig.s32WorkingViNum = 1;
    g_stViConfig.astViInfo[0].stDevInfo.ViDev = 0;
    g_stViConfig.astViInfo[0].stPipeInfo.aPipe[0] = 0;
    g_stViConfig.astViInfo[0].stChnInfo.ViChn = 0;
    g_stViConfig.astViInfo[0].stChnInfo.enDynamicRange = DYNAMIC_RANGE_SDR8;
    g_stViConfig.astViInfo[0].stChnInfo.enPixFormat = enPixFormat;
    s32Ret = __SAMPLE_VENC_VI_Init(&g_stViConfig, g_u32SupplementConfig);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("Init VI err for %#x!\n", s32Ret);
        return HI_FAILURE;
    }

    s32Ret = __SAMPLE_VENC_VPSS_Init(VpssGrp, abChnEnable, DYNAMIC_RANGE_SDR8, enPixFormat,
                                     stSize, g_stViConfig.astViInfo[0].stSnsInfo.enSnsType);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("Init VPSS err for %#x!\n", s32Ret);
        goto EXIT_VI_STOP;
    }

    s32Ret = SAMPLE_COMM_VI_Bind_VPSS(0, 0, VpssGrp);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("VI Bind VPSS err for %#x!\n", s32Ret);
        goto EXIT_VPSS_STOP;
    }

    // if (__encoder_property.format != V4L2_PIX_FMT_YUYV && __encoder_property.format != V4L2_PIX_FMT_YUV420)
    // {
        enRcMode = SAMPLE_RC_CBR;
        if (enPayLoad == PT_H264)
        {
            enRcMode = SAMPLE_RC_AVBR;
        }

        /******************************************
         start stream venc
        ******************************************/
        s32Ret = SAMPLE_COMM_VENC_GetGopAttr(enGopMode, &stGopAttr);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("Venc Get GopAttr for %#x!\n", s32Ret);
            goto EXIT_VI_VPSS_UNBIND;
        }
        if (VENC_GOPMODE_BIPREDB == enGopMode)
        {
            SAMPLE_PRT("BIPREDB and ADVSMARTP not support lowdelay!\n");
            goto EXIT_VI_VPSS_UNBIND;
        }

        s32Ret = SAMPLE_COMM_VENC_Start(VencChn[0], enPayLoad, g_enSize, enRcMode, u32Profile, bRcnRefShareBuf, &stGopAttr);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("Venc Start failed for %#x!\n", s32Ret);
            goto EXIT_VI_VPSS_UNBIND;
        }

        s32Ret = SAMPLE_COMM_VPSS_Bind_VENC(VpssGrp, VpssChn, VencChn[0]);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("Venc Get GopAttr failed for %#x!\n", s32Ret);
            goto EXIT_VENC_STOP;
        }

        /******************************************
         stream save process
        ******************************************/
        g_s32ChnNum = s32ChnNum;
        s32Ret = __SAMPLE_COMM_VENC_StartGetStream(VencChn, s32ChnNum);
        if (HI_SUCCESS != s32Ret)
        {
            SAMPLE_PRT("Start Venc failed!\n");
            goto EXIT_VPSS_VENC_UNBIND;
        }
    // }
    // else
    // {
    //     s32Ret = __SAMPLE_StartGetYUV(VpssGrp, VpssChn);
    //     if (HI_SUCCESS != s32Ret)
    //     {
    //         SAMPLE_PRT("Start get YUV failed!\n");
    //         goto EXIT_VI_VPSS_UNBIND;
    //     }
    // }

    return s32Ret;

EXIT_VPSS_VENC_UNBIND:
    SAMPLE_COMM_VPSS_UnBind_VENC(VpssGrp, VpssChn, VencChn[0]);
EXIT_VENC_STOP:
    SAMPLE_COMM_VENC_Stop(VencChn[0]);
EXIT_VI_VPSS_UNBIND:
    SAMPLE_COMM_VI_UnBind_VPSS(0, 0, VpssGrp);
EXIT_VPSS_STOP:
    SAMPLE_COMM_VPSS_Stop(VpssGrp, abChnEnable);
EXIT_VI_STOP:
    SAMPLE_COMM_VI_StopVi(&g_stViConfig);
    SAMPLE_COMM_SYS_Exit();
    return s32Ret;
}

/*******************************************************
 * Processing Unit Operation Functions
 *******************************************************/
static uint16_t _venc_brightness_get(HI_VOID)
{
    ISP_CSC_ATTR_S stCSCAttr;

    HI_MPI_ISP_GetCSCAttr(0, &stCSCAttr);

    return (uint16_t)stCSCAttr.u8Luma;
}

static uint16_t _venc_contrast_get(HI_VOID)
{
    ISP_CSC_ATTR_S stCSCAttr;

    HI_MPI_ISP_GetCSCAttr(0, &stCSCAttr);

    return (uint16_t)stCSCAttr.u8Contr;
}

static uint16_t _venc_hue_get(HI_VOID)
{
    ISP_CSC_ATTR_S stCSCAttr;

    HI_MPI_ISP_GetCSCAttr(0, &stCSCAttr);

    return (uint16_t)stCSCAttr.u8Hue;
}

static uint8_t _venc_power_line_frequency_get(HI_VOID)
{
    ISP_EXPOSURE_ATTR_S stExpAttr;

    HI_MPI_ISP_GetExposureAttr(0, &stExpAttr);

    if (stExpAttr.stAuto.stAntiflicker.u8Frequency != 50 &&
        stExpAttr.stAuto.stAntiflicker.u8Frequency != 60)
    {
        return 0;
    }

    return (stExpAttr.stAuto.stAntiflicker.u8Frequency == 50) ? 0x1 : 0x2;
}

static uint16_t _venc_saturation_get(HI_VOID)
{
    ISP_CSC_ATTR_S stCSCAttr;

    HI_MPI_ISP_GetCSCAttr(0, &stCSCAttr);

    return (uint16_t)stCSCAttr.u8Satu;
}

static uint8_t _venc_white_balance_temperature_auto_get(HI_VOID)
{
    ISP_WB_ATTR_S stWBAttr;

    HI_MPI_ISP_GetWBAttr(0, &stWBAttr);

    return (stWBAttr.enOpType == OP_TYPE_AUTO) ? 0x1 : 0x0;
}

static uint16_t _venc_white_balance_temperature_get(HI_VOID)
{
    ISP_WB_INFO_S stWBInfo;

    HI_MPI_ISP_QueryWBInfo(0, &stWBInfo);

    return (uint16_t)stWBInfo.u16ColorTemp;
}

static HI_VOID _venc_brightness_set(uint16_t v)
{
    ISP_CSC_ATTR_S stCSCAttr;

    HI_MPI_ISP_GetCSCAttr(0, &stCSCAttr);
    stCSCAttr.u8Luma = v;
    HI_MPI_ISP_SetCSCAttr(0, &stCSCAttr);
}

static HI_VOID _venc_contrast_set(uint16_t v)
{
    ISP_CSC_ATTR_S stCSCAttr;

    HI_MPI_ISP_GetCSCAttr(0, &stCSCAttr);
    stCSCAttr.u8Contr = v;
    HI_MPI_ISP_SetCSCAttr(0, &stCSCAttr);
}

static HI_VOID _venc_hue_set(uint16_t v)
{
    ISP_CSC_ATTR_S stCSCAttr;

    HI_MPI_ISP_GetCSCAttr(0, &stCSCAttr);
    stCSCAttr.u8Hue = v;
    HI_MPI_ISP_SetCSCAttr(0, &stCSCAttr);
}

static HI_VOID _venc_power_line_frequency_set(uint8_t v)
{
    HI_S32 s32Ret;
    ISP_EXPOSURE_ATTR_S stExpAttr;

    HI_MPI_ISP_GetExposureAttr(0, &stExpAttr);
    if (0 == v)
    {
        stExpAttr.stAuto.stAntiflicker.bEnable = HI_FALSE;
    }
    else if (1 == v)
    {
        stExpAttr.stAuto.stAntiflicker.bEnable = HI_TRUE;
        stExpAttr.stAuto.stAntiflicker.u8Frequency = 50;
    }
    else if (2 == v)
    {
        stExpAttr.stAuto.stAntiflicker.bEnable = HI_TRUE;
        stExpAttr.stAuto.stAntiflicker.u8Frequency = 60;
    }

    s32Ret = HI_MPI_ISP_SetExposureAttr(0, &stExpAttr);
    if (s32Ret != HI_SUCCESS)
    {
        printf("HI_MPI_ISP_SetExposureAttr err 0x%x\n", s32Ret);
    }
}

static HI_VOID _venc_saturation_set(uint16_t v)
{
    ISP_CSC_ATTR_S stCSCAttr;

    HI_MPI_ISP_GetCSCAttr(0, &stCSCAttr);
    stCSCAttr.u8Satu = v;
    HI_MPI_ISP_SetCSCAttr(0, &stCSCAttr);
}

static HI_VOID _venc_white_balance_temperature_auto_set(uint8_t v)
{
    ISP_WB_ATTR_S stWBAttr;

    HI_MPI_ISP_GetWBAttr(0, &stWBAttr);
    stWBAttr.enOpType = (v == 1) ? OP_TYPE_AUTO : OP_TYPE_MANUAL;
    HI_MPI_ISP_SetWBAttr(0, &stWBAttr);
}

static HI_VOID _venc_white_balance_temperature_set(uint16_t v)
{
    ISP_WB_INFO_S stWBInfo;
    ISP_WB_ATTR_S stWBAttr;
    HI_U16 u16ColorTemp;
    HI_U16 u16AWBGain[4];

    HI_MPI_ISP_QueryWBInfo(0, &stWBInfo);
    HI_MPI_ISP_GetWBAttr(0, &stWBAttr);

    u16ColorTemp = v;
    HI_MPI_ISP_CalGainByTemp(0, &stWBAttr, u16ColorTemp, 0, u16AWBGain);

    stWBAttr.enOpType = OP_TYPE_MANUAL;
    memcpy(&stWBAttr.stManual, u16AWBGain, sizeof(stWBAttr.stManual));

    HI_MPI_ISP_SetWBAttr(0, &stWBAttr);
}
/* Processing Unit Operation Functions End */

/*******************************************************
 * Input Terminal Operation Functions
 *******************************************************/
static uint8_t _venc_exposure_auto_mode_get(HI_VOID)
{
    ISP_EXPOSURE_ATTR_S stExpAttr;

    HI_MPI_ISP_GetExposureAttr(0, &stExpAttr);

    return (stExpAttr.enOpType == OP_TYPE_AUTO) ? 0x02 : 0x04;
}

static uint32_t _venc_exposure_ansolute_time_get(HI_VOID)
{
    ISP_EXPOSURE_ATTR_S stExpAttr;

    HI_MPI_ISP_GetExposureAttr(0, &stExpAttr);

    return stExpAttr.stManual.u32ExpTime;
}

static HI_VOID _venc_exposure_auto_mode_set(uint8_t v)
{
    ISP_EXPOSURE_ATTR_S stExpAttr;

    HI_MPI_ISP_GetExposureAttr(0, &stExpAttr);
    stExpAttr.enOpType = (v == 4) ? OP_TYPE_MANUAL : OP_TYPE_AUTO;
    HI_MPI_ISP_SetExposureAttr(0, &stExpAttr);
}

static HI_VOID _venc_exposure_ansolute_time_set(uint32_t v)
{
    ISP_EXPOSURE_ATTR_S stExpAttr;

    HI_MPI_ISP_GetExposureAttr(0, &stExpAttr);
    stExpAttr.stManual.u32ExpTime = v * 100;
    stExpAttr.stManual.enExpTimeOpType = OP_TYPE_MANUAL;
    HI_MPI_ISP_SetExposureAttr(0, &stExpAttr);
}
/* Input Terminal Operation Functions End */

/*******************************************************
 * Stream Control Operation Functions
 *******************************************************/
static HI_S32 sample_venc_set_idr(HI_VOID)
{
    return HI_MPI_VENC_RequestIDR(1, HI_TRUE);
}

HI_S32 sample_venc_init(HI_VOID)
{
    HI_S32 s32Ret = HI_FAILURE;
    SAMPLE_SNS_TYPE_E enSnsType = SENSOR0_TYPE;

    s32Ret = __SAMPLE_VENC_SYS_Init(g_u32SupplementConfig, enSnsType);
    if (s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("Init SYS err for %#x!\n", s32Ret);
        return s32Ret;
    }

    return 0;
}

HI_S32 sample_venc_startup(HI_VOID)
{
    HI_S32 ret = HI_FAILURE;

    __started = 1;

    ret = __SAMPLE_VENC_NORMALP_CLASSIC();
    HI_MPI_VENC_RequestIDR(0, HI_TRUE);

    return ret;
}

HI_S32 sample_venc_shutdown(HI_VOID)
{
    HI_S32 VpssGrp = 0;
    HI_S32 VpssChn = 1;
    HI_S32 VencChn = 0;
    HI_BOOL abChnEnable[3] = {0, 1, 0};

    if (!__started)
    {
        return 0;
    }

    // if (__encoder_property.format != V4L2_PIX_FMT_YUYV && __encoder_property.format != V4L2_PIX_FMT_YUV420)
    // {
        __SAMPLE_COMM_VENC_StopGetStream();
        SAMPLE_COMM_VPSS_UnBind_VENC(VpssGrp, VpssChn, VencChn);
        SAMPLE_COMM_VENC_Stop(VencChn);
    // }
    // else
    // {
    //     __SAMPLE_StopGetYUV();
    // }

    SAMPLE_COMM_VI_UnBind_VPSS(0, 0, VpssGrp);
    SAMPLE_COMM_VPSS_Stop(0, abChnEnable);
    SAMPLE_COMM_VI_StopVi(&g_stViConfig);

    __started = 0;

    return HI_SUCCESS;
}
